package com.xexun.gpstracker.common;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.model.LatLng;
import com.xexun.gpstracker.inter.CommCallback;
import com.xexun.gpstracker.inter.LocationProvider;
import com.xexun.gpstracker.inter.NotifyCallback;
import com.xexun.gpstracker.inter.NotifyCallbackEx;
import com.xexun.gpstracker.util.CommUtil;
import com.xexun.gpstracker.vo.GTLatLng;

/**
 * Created by HH
 * Date: 2015/6/24 0024
 * Time: 下午 7:51
 */
public class LocationProviderGMAP implements LocationProvider, LocationListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
    private Context context;
    private GoogleApiClient googleApiClient;
    private static Map<String, NotifyCallback> callbackMap;
    private static LinkedList<NotifyCallback> callbackList;
    private static LinkedList<NotifyCallbackEx> callbackListEx;

    public static boolean isLocationing = false;

    private static LocationProviderGMAP instance;

    private LocationProviderGMAP() {
    }

    public static LocationProviderGMAP getInstance(Context context) {
        if (instance == null) {
            instance = new LocationProviderGMAP(context);
        }
        return instance;
    }

    private LocationProviderGMAP(Context context) {
        this.context = context;
    }

    @Override
    public synchronized void startLocation() {
        if (googleApiClient != null && googleApiClient.isConnected()) {
            onConnected(null);
            return;
        }
        if (googleApiClient == null) {
            googleApiClient = new GoogleApiClient.Builder(context)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }
        if (!googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
            googleApiClient.connect();
        }
        isLocationing = true;
    }

    public void startLocation(NotifyCallback callback) {
        if (callbackList == null) {
            callbackList = new LinkedList<>();
        }
        callbackList.add(callback);
        startLocation();
    }

    public void startLocation(NotifyCallbackEx callback) {
        if (callbackListEx == null) {
            callbackListEx = new LinkedList<>();
        }
        callbackListEx.add(callback);
        startLocation();
    }

    public LocationProvider setNotify(String key, NotifyCallback callback) {
        if (callbackMap == null) {
            callbackMap = new ConcurrentHashMap<>();
        }
        callbackMap.put(key, callback);
        return this;
    }

    public void removeNotify(String key) {
        if (callbackMap != null) {
            callbackMap.remove(key);
        }
    }

    public void clearNotify() {
        if (callbackMap != null) {
            callbackMap.clear();
        }
    }

    public void stop() {
        if (googleApiClient != null) {
            googleApiClient.disconnect();
            googleApiClient.unregisterConnectionCallbacks(this);
            googleApiClient.unregisterConnectionFailedListener(this);
        }
        googleApiClient = null;
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location == null) {
            return;
        }
        isLocationing = false;
        GTLatLng latLng = new GTLatLng(location.getLatitude(), location.getLongitude());
        Log.d(Constants.TAG, "当前位置：" + location.getLatitude() + "," + location.getLongitude());
        if (callbackMap != null && !callbackMap.isEmpty()) {
            for (Map.Entry<String, NotifyCallback> entry : callbackMap.entrySet()) {
                try {
                    entry.getValue().execute(latLng);
                } catch (Exception e) {
                    Log.w(Constants.TAG, "回调出错，有可能是执行环境已改变", e);
                }
            }
        }
        if (CommUtil.isNotEmpty(callbackList)) {
            NotifyCallback callback;
            while ((callback = callbackList.poll()) != null) {
                try {
                    callback.execute(latLng);
                } catch (Exception e) {
                    Log.w(Constants.TAG, "回调出错，有可能是执行环境已改变", e);
                }
            }
        }
        if (CommUtil.isNotEmpty(callbackListEx)) {
            NotifyCallbackEx callback;
            while ((callback = callbackListEx.poll()) != null) {
                try {
                    callback.execute(location);
                } catch (Exception e) {
                    Log.w(Constants.TAG, "回调出错，有可能是执行环境已改变", e);
                }
            }
        }
//        stop();
    }

    @Override
    public void onConnected(Bundle bundle) {
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        Location location = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
        onLocationChanged(location);
    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        googleApiClient.reconnect();
    }

    public void reverseGeoCode(LatLng latLng, CommCallback callback) {
        ReverseGeoCoder coder = new ReverseGeoCoder(latLng, callback);
        Thread thread = new Thread(coder);
        thread.start();
    }

    private class ReverseGeoCoder implements Runnable {
        private LatLng latLng;
        private CommCallback callback;

        ReverseGeoCoder(LatLng latLng, CommCallback callback) {
            this.latLng = latLng;
            this.callback = callback;
        }

        @Override
        public void run() {
            String addr = Constants.DEFAULT_BLANK;
            try {
                Geocoder geocoder = new Geocoder(context, Locale.getDefault());
                Log.i(LocationProviderGMAP.class.getName(),"反编码的坐标："+latLng.latitude+","+latLng.longitude);
                List<Address> addresses = geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1);
                if (CommUtil.isNotEmpty(addresses)) {
                    Address address = addresses.get(0);
                    StringBuilder sb = new StringBuilder();
                    String feature=address.getFeatureName();
                    if(CommUtil.isNotBlank(feature)){
                        sb.append(feature).append(' ');
                    }
                    for (int i = address.getMaxAddressLineIndex(); i >= 0; i--) {
                        String addrs=address.getAddressLine(i);
                        if(CommUtil.isNotBlank(addrs)){
                            sb.append(address.getAddressLine(i)).append(' ');
                        }
                    }
                    if (sb.length() > 0) {
                        sb.deleteCharAt(sb.length() - 1);
                    }
                    addr = sb.toString();
                }
            } catch (IOException e) {
                Log.e(LocationProviderGMAP.class.getName(), "获取地名失败", e);
            }
            callback.execute(addr);
        }
    }

    @Override
    public void geoCodeSearch(GTLatLng latLng, CommCallback callback) {
        LatLng ll=new LatLng(latLng.getLatitude(),latLng.getLongitude());
        reverseGeoCode(ll,callback);
    }
}
